package com.mls.guardian.appst.shell;

import com.mls.guardian.appst.chain.TaskResult;
import com.mls.guardian.common.OSPlatform;
import com.mls.guardian.common.OSUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.List;

/**
 * Created by jl on 15/9/18.
 */
public class ShellScriptExecutor {
    private static final Logger log = LoggerFactory.getLogger(ShellScriptExecutor.class);
    private static final long STOP_TIMEOUT_ADDITION = 2000L;

    private Process process = null;
    private boolean isInput = true;
    private boolean isError = false;
    private boolean isStop = false;

    private String oldString = "";
    private String newString = "";
    private String errorString = "";

    private List<String> list;
    private int index = 0;

    private Thread threadMonitor;
    private Thread threadInput;
    private Thread threadOutput;
    private Thread threadError;

    private ShellResultListener listener;

    /**
     * 可以使单条命令,也可以是一个脚本文件；
     * 注意一定要有输出结果,如果没有输出，可以放到一个脚本文件，人为叫一个输出结果;
     * 如果没有输出结果会导致输出数据线程一直死等,无法输入下一条命令
     *
     * @param list 命令集合
     */
    public ShellScriptExecutor(List<String> list) {
        this.list = list;
        try {
            if (OSUtil.getOSname() == OSPlatform.Windows) {
                this.process = Runtime.getRuntime().exec("cmd");
            } else {
                this.process = Runtime.getRuntime().exec("/bin/bash");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        this.threadMonitor = createMonitor();
        this.threadInput = createInput(this.process.getOutputStream());
        this.threadOutput = createOutput(this.process.getInputStream());
        this.threadError = createError(this.process.getErrorStream());

    }

    public synchronized void setOutput(String msg) {
        newString += msg;
    }

    public synchronized void checkOutput() {
        if (oldString.equals(newString) && oldString.length() > 0) {
            setInput(true);
            newString = "";
        } else {
            oldString = newString;
        }
    }

    public synchronized String getNextCmd() {
        String rtn = "";
        if (index < list.size()) {
            rtn = list.get(index);
            index++;
        }
        return rtn;
    }

    private Thread createMonitor() {
        return new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    if (isStop() == true) {
                        if (listener != null) {
                            TaskResult result = new TaskResult();
                            result.setOut("");
                            result.setError(getErrorString());
                            result.setSuc(isError() == true ? false : true);
                            listener.doResult(result);
                        }
                        System.out.println("---结束啦monitor---");
                        break;
                    }
                    try {
                        Thread.sleep(10L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    if (isInput == false) {
                        checkOutput();
                    }
                }
            }
        });
    }

    private Thread createInput(OutputStream outputStream) {
        return new Thread(new Runnable() {
            @Override
            public void run() {
                PrintWriter pw = new PrintWriter(outputStream, true);
                while (true) {
                    try {
                        Thread.sleep(1 * 500L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    if (isInput() == true) {
                        String cmd = getNextCmd();
                        if (isError() == true) {
                            cmd = "exit";
                        }
                        //System.out.println("input_cmd:" + cmd);
                        if (cmd.length() == 0 || cmd.equals("exit") || cmd.equals("quit")) {
                            setStop(true);
                            setInput(false);
                            System.out.println("---结束啦input---");
                            break;
                        } else {
                            setInput(false);
                            pw.println(cmd);
                        }
                    }
                }
                pw.close();
            }
        });
    }

    private Thread createOutput(InputStream inputStream) {
        return new Thread(new Runnable() {
            @Override
            public void run() {
                InputStreamReader reader = null;
                try {
                    reader = new InputStreamReader(inputStream, "GBK");
                } catch (UnsupportedEncodingException e1) {
                    e1.printStackTrace();
                }
                char[] buf = new char[1024];
                int size;
                while (true) {
                    try {
                        size = reader.read(buf);
                    } catch (IOException e) {
                        e.printStackTrace();
                        break;
                    }
                    if (size != -1) {
                        setOutput(new String(buf, 0, size));
                        System.out.print(new String(buf, 0, size));
                    } else {
                        System.out.println("---结束了output---");
                        break;
                    }
                }
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private Thread createError(InputStream inputStream) {
        return new Thread(new Runnable() {
            @Override
            public void run() {
                InputStreamReader reader = null;
                try {
                    reader = new InputStreamReader(inputStream, "GBK");
                } catch (UnsupportedEncodingException e1) {
                    e1.printStackTrace();
                }
                char[] buf = new char[1024];
                int size;
                while (true) {
                    try {
                        size = reader.read(buf);
                    } catch (IOException e) {
                        e.printStackTrace();
                        break;
                    }
                    if (size != -1) {
                        setError(true, new String(buf, 0, size));
                        setOutput(new String(buf, 0, size));
                        System.out.print(new String(buf, 0, size));
                    } else {
                        System.out.println("---结束了Error---");
                        break;
                    }
                }

                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public void execute() {
        if (this.threadMonitor != null) {
            this.threadMonitor.start();
        }
        if (this.threadInput != null) {
            this.threadInput.start();
        }
        if (this.threadOutput != null) {
            this.threadOutput.start();
        }
        if (this.threadError != null) {
            this.threadError.start();
        }
    }

    public void execute(ShellResultListener listener) {
        this.listener = listener;
        this.execute();
    }

    public void stop() {
        stopThread(this.threadMonitor, STOP_TIMEOUT_ADDITION);
        stopThread(this.threadInput, STOP_TIMEOUT_ADDITION);
        stopThread(this.threadOutput, STOP_TIMEOUT_ADDITION);
        stopThread(this.threadError, STOP_TIMEOUT_ADDITION);
    }

    protected void stopThread(Thread thread, final long timeout) {
        if (thread != null) {
            try {
                if (timeout == 0) {
                    thread.join();
                } else {
                    final long timeToWait = timeout + STOP_TIMEOUT_ADDITION;
                    final long startTime = System.currentTimeMillis();
                    thread.join(timeToWait);
                    if (!(System.currentTimeMillis() < startTime + timeToWait)) {
                        final String msg = "The stop timeout of " + timeout + " ms was exceeded";
                        log.debug(msg);
                    }
                }
            } catch (final InterruptedException e) {
                thread.interrupt();
            }
        }
    }


    public boolean isInput() {
        return isInput;
    }

    public void setInput(boolean isInput) {
        this.isInput = isInput;
    }

    public boolean isError() {
        return isError;
    }

    public void setError(boolean isError) {
        this.isError = isError;
    }

    public void setError(boolean isError, String errorString) {
        this.isError = isError;
        this.errorString = errorString;
    }

    public String getErrorString() {
        return errorString;
    }

    public void setErrorString(String errorString) {
        this.errorString = errorString;
    }

    public boolean isStop() {
        return isStop;
    }

    public void setStop(boolean isStop) {
        this.isStop = isStop;
    }


    public static boolean execute(String command) throws IOException {
//        File file = new File("/Users/MLS/Documents/projectCode/wms/Java/WMSPlatform/BaseComServer/deploy.sh");
//        CommandLine oCmdLine = new CommandLine(file);
//        DefaultExecutor oDefaultExecutor = new DefaultExecutor();
//        oDefaultExecutor.setExitValue(0);
//
//        oDefaultExecutor.execute(oCmdLine);

        return true;
    }


    public static void main(String a[]) throws IOException {
        ShellScriptExecutor.execute("cd /Users/MLS/Documents/projectCode/wms/Java/WMSPlatform/BaseComServer");
//        ShellScriptExcutor.execute("");
    }
}
